#ifndef SCENE_H
#define SCENE_H

class Mesh;

///////////////////////////////////////////////////////////////////////////////
// Material
///////////////////////////////////////////////////////////////////////////////

class Material
{
public:
								Material( void );
	unsigned int				GetTexture( void ) const ;
	void						SetTexture( unsigned int texture );	
	const cg::Vec4&				GetAmbientAndDiffuseColor( void ) const;
	void						SetAmbientAndDiffuseColor( const cg::Vec4& color );
private:
	int							texture;
	cg::Vec4					ambientAndDiffuseColor;
};

CG_INLINE Material::Material( void )
{
	texture = -1;
	SetAmbientAndDiffuseColor( cg::Color::GREY );
}

CG_INLINE unsigned int Material::GetTexture( void ) const
{
	return texture;
}

CG_INLINE void Material::SetTexture( unsigned int texture )
{
	this->texture = texture;
}
		
CG_INLINE const cg::Vec4& Material::GetAmbientAndDiffuseColor( void ) const
{
	return ambientAndDiffuseColor;
}

CG_INLINE void Material::SetAmbientAndDiffuseColor( const cg::Vec4& color )
{
	ambientAndDiffuseColor = color;
}

///////////////////////////////////////////////////////////////////////////////
// Entity
///////////////////////////////////////////////////////////////////////////////

class Entity
{
public:
								Entity( Mesh* mesh );
	cg::Frame					frame;
	Material					material;
	//
	void						Render();

private:
	Mesh *						mesh;
};

CG_INLINE Entity::Entity( Mesh* mesh )
{
	assert( mesh );
	this->mesh = mesh;
}

///////////////////////////////////////////////////////////////////////////////
// Light
///////////////////////////////////////////////////////////////////////////////

class Light
{
public:

							Light( int lightId );
	//
	cg::Vec4				GetPosition( void ) const;
	void					SetPosition( const cg::Vec4& position );
	void					Update( void );
	//
	bool					IsEnabled( void ) const;
	void					Enable( void );
	void					Disable( void );

private:
	cg::Vec4				position;
	int						lightId;
	bool					state;
};


CG_INLINE Light::Light( int lightId )
{
	this->lightId = lightId;
	Disable();
}

CG_INLINE cg::Vec4 Light::GetPosition( void ) const
{
	return position;
}

CG_INLINE void Light::SetPosition( const cg::Vec4& position )
{
	this->position = position;
}

CG_INLINE void Light::Update( void )
{
	glLightfv( lightId, GL_POSITION, this->position.toFloat() );
}

CG_INLINE bool Light::IsEnabled( void ) const
{
	return state;
}

CG_INLINE void Light::Enable( void )
{
	state = true;
	glEnable( lightId );
}

CG_INLINE void Light::Disable( void )
{
	state = false;
	glDisable( lightId );
}

///////////////////////////////////////////////////////////////////////////////
// Scene
///////////////////////////////////////////////////////////////////////////////

class Scene
{
public:	
											Scene( void );											
											~Scene( void );
	// Misc	
	void									Init( void );												
	void									Render( void ); // renders all entities	
	// Entity
	int										AddEntity( Entity* entity ); // returns key for entity	
	Entity*									GetEntity( int key ); // returns entity for given key or NULL	
	// Ligting	
	void									AmbientLight( const cg::Vec4& color );
	Light*									GetLight( int id ); // NULL is return when id doesn't match any light
	int										GetDisabledLightId( void ) const ; // -1 means all lights are enabled
	void									DisableLighting( void );
	void									EnableLighting( void );		

private:	
	typedef std::map< int, Entity* >		Entities;
	typedef std::map< int, Light* >			Lights;
	
	Entities								entities;
	Lights									lights;
	int										entityIndex;
};

CG_INLINE Scene::Scene( void )
{
	// reset entity indexing
	entityIndex = 0;
	// create all lights
	for( int i = GL_LIGHT0; i <= GL_LIGHT7; ++i ) {
		lights[ i ] = new Light( i );
	}
}

CG_INLINE Scene::~Scene( void )
{
	// free entities
	for( Entities::iterator it = entities.begin(); it != entities.end(); ++it ) {
		delete it->second;
	}
	// free lights
	for( Lights::iterator it = lights.begin(); it != lights.end(); ++it ) {
		delete it->second;
	}
}

CG_INLINE void Scene::Init( void )
{
	EnableLighting();
}

CG_INLINE void Scene::Render( void )
{
	for( Lights::iterator it = lights.begin(); it != lights.end(); ++it ) {
		it->second->Update();
	}
	glEnable( GL_COLOR_MATERIAL );
	glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
	for( Entities::iterator it = entities.begin(); it != entities.end(); ++it ) {
		it->second->Render();
	}
}

///////////////////////////////////////////////////////////////////////////
// Entity
///////////////////////////////////////////////////////////////////////////
	
CG_INLINE int Scene::AddEntity( Entity* entity )
{
	assert( entity );
	entities[ entityIndex ] = entity;
	return entityIndex++;
}

CG_INLINE Entity* Scene::GetEntity( int key )
{
	Entities::iterator it = entities.find( key );
	if ( it != entities.end() )
		return it->second;
	return NULL;
}

///////////////////////////////////////////////////////////////////////////
// Ligting
///////////////////////////////////////////////////////////////////////////

CG_INLINE void Scene::AmbientLight( const cg::Vec4& color )
{
	glLightModelfv( GL_LIGHT_MODEL_AMBIENT, color.toFloat() );
}

CG_INLINE Light* Scene::GetLight( int id )
{
	Lights::iterator it = lights.find( id );
	if ( it == lights.end() )
		return NULL;
	return it->second;	
}

CG_INLINE int Scene::GetDisabledLightId( void ) const
{
	for( Lights::const_iterator it = lights.begin(); it != lights.end(); ++it ) {
		if ( ! it->second->IsEnabled() )
			return it->first;
	}	
	return -1;
}

CG_INLINE void Scene::DisableLighting( void )
{
	glDisable( GL_LIGHTING );
}

CG_INLINE void Scene::EnableLighting( void )
{
	glEnable( GL_LIGHTING );
}

#endif